package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/spf13/pflag"
)

const INT_MAX = int(^uint(0) >> 1)

type Selpg struct {
	start     *int    // -s参数
	end       *int    // -e参数
	page_len  *int    // -l参数
	page_type *bool   // -f参数
	file      string  // 输入文件名
	dest      *string // -d参数值（打印目的地）
}

var selpg Selpg

func errors(e string) {
	if err := fmt.Errorf(e); err != nil {
		panic(e)
	}
}

func Init() {
	selpg.start = pflag.IntP("start", "s", -1, "start page")
	selpg.end = pflag.IntP("end", "e", -1, "end page")
	selpg.page_type = pflag.BoolP("form_feed", "f", false, "delimited by form feeds")
	selpg.page_len = pflag.IntP("limit", "l", 72, "delimited by fixed page length")
	selpg.dest = pflag.StringP("dest", "d", "", "target printer")
	selpg.file = ""
	pflag.Parse()
}

func load() {
	if *selpg.start <= 0 || *selpg.end <= 0 {
		errors("You are expected to input the start_page_num and end_page_num which will be greater than zero")
	}
	if *selpg.start > INT_MAX || *selpg.end > INT_MAX {
		errors("The start_page_num and end_page_num can't be greater than INT_MAX: " + strconv.Itoa(INT_MAX))
	}
	if *selpg.start > *selpg.end {
		errors("The end_page_num can't be greater than start_page_num")
	}
	if *selpg.page_len <= 0 {
		errors("The -l limit can't be less than or equal to zero")
	}
	if pflag.NArg() > 1 {
		errors("The num of file can't be more than one")
	} else if pflag.NArg() == 1 {
		selpg.file = pflag.Args()[0]
	}
}

func read(w *io.PipeWriter, inputFile *os.File) {
	input := bufio.NewReader(inputFile)
	page := 0
	line := 0
	flag := '\n'
	str_page := ""

	if *selpg.page_type {
		flag = '\f'
	}

	for page < *selpg.end {
		str_line, err := input.ReadString(byte(flag))
		if err == io.EOF {
			if page < *selpg.start-1 {
				break
			}
			if str_line != "" && str_line != "\n" && str_line != "\f" {
				fmt.Fprint(w, str_line)
			} else if str_page != "" && str_page != "\n" && str_page != "\f" {
				str_page = strings.Replace(str_page, "\f", "", -1)
				fmt.Fprint(w, str_page)
			}
			break
		}
		if flag == '\n' {
			str_page += str_line
			line++
			if line >= *selpg.page_len {
				page++
				line = 0
				if page >= *selpg.start {
					str_page = strings.Replace(str_page, "\f", "", -1)
					fmt.Fprint(w, str_page+"\f")
				}
				str_page = ""
			}
		} else {
			page++
			if page >= *selpg.start {
				fmt.Fprint(w, str_line)
			}
		}
	}

	w.Close()
}

func execute() {
	var inputFile *os.File
	var outputFile *os.File
	var cmd *exec.Cmd
	r, w := io.Pipe()
	buf := new(bytes.Buffer)

	if selpg.file != "" {
		var err error
		inputFile, err = os.Open(selpg.file)
		if err != nil {
			errors("Failed to open the file\n")
		}
	} else {
		inputFile = os.Stdin
	}

	if *selpg.dest != "" {
		fmt.Printf("Printer: %s\n", *selpg.dest)
		cmd = exec.Command("lp", "-d", *selpg.dest)
	} else {
		outputFile = os.Stdout
	}

	go read(w, inputFile)
	buf.ReadFrom(r)

	if cmd != nil {
		var Err bytes.Buffer
		cmd.Stdin = strings.NewReader(buf.String())
		cmd.Stderr = &Err
		err := cmd.Run()
		if err != nil {
			errors(fmt.Sprint(err) + ": " + Err.String())
		}
	} else if outputFile != nil {
		outputFile.WriteString(buf.String())
	}

	inputFile.Close()
}

func SelpgRun() {
	Init()
	load()
	execute()
}

func main() {
	SelpgRun()
}
